import java.util.ArrayList;
import java.util.Map;
import java.util.Scanner;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

enum QueryType{
    ID,Name,Subject
};

public class GradeTable {
    private ArrayList<Course>table;
    private ArrayList<Stud>StuList;
    private TreeMap<String,String>IdtoName;
    public GradeTable(){
        table=new ArrayList<>();
        StuList=new ArrayList<>();
        IdtoName=new TreeMap<>();
    }
    public static void main(String[] args){
        GradeTable gt=new GradeTable();
        gt.addCourse("Java");
        gt.addStudent(new Stud("张三","1234"),"Java",89);
        gt.addStudent(new Stud("张三","1235"),"C++",89);
        gt.addStudent(new Stud("张三","1236"),"C++",9);
        gt.addStudent(new Stud("张三","1326"),"C++",59);
        gt.addStudent(new Stud("张三","1242"),"C",29);
        gt.addStudent(new Stud("张三","2234"),"Discrete Math",99);
        gt.addStudent(new Stud("张三","3234"),"C++",0);
        gt.addStudent(new Stud("张三","4234"),"C++",100);
        System.out.println("info about the student whose ID is 3234");
        gt.showQuery("3234",QueryType.ID);
        if(gt.showNoPass("C++")){
            System.out.println("C++有没过的");
        }
        if(!gt.showNoPass("Java")){
            System.out.println("Java没有没过的");
        }
        System.out.println("Show info about the top student of C++");
        if(!gt.showTop("C++")){
            System.out.println("C++ not exists now");
        }
        System.out.println("Show info about the top student of Python");
        if(!gt.showTop("Python")){
            System.out.println("Python not exists now");
        }
        System.out.println("Average score of C++ is :");
        System.out.println(gt.courseAverage("C++"));
        System.out.println("The Score Distribution of C++ is :");
        gt.showCourseStage("C++");
        gt.Table();
    }
    public void Table(){
        Scanner scanner=new Scanner(System.in);
        String rel="";
        do{
            GradeTable.TableShow();
            do{
                if(!rel.isEmpty())
                    System.out.println("请输入正确的选择");
                System.out.print("请输入你的选择 : ");
                rel=scanner.next();
            }while(!ValidChoice(rel));
        }while(operate(rel));
    }
    private boolean operate(String op){
        switch(op.toCharArray()[0]-'0'){
            case 1,2 -> System.out.println("not support now");
            case 3 -> table.add(new Course());
            case 4 -> addStudent(new Stud(),new Course().getCourseName(),Course.getValidScore());
            case 5 -> {
                showTop(new Course().getCourseName());
            }
            case 6 -> {
                String c=new Course().getCourseName();
                System.out.println("The course " + c + "'s average score is " + courseAverage(c));
            }
            case 7 -> {
                System.out.println("Exit successfully!");
                return false;
            }
            default -> throw new IllegalStateException("Unexpected value: " + op.toCharArray()[0]);
        }return true;
    }
    public static boolean ValidChoice(String r){
        return r.length()==1 &&
                r.toCharArray()[0]>'0' && r.toCharArray()[0]<'8';
    }
    public static void TableShow(){
        final char EOF='\n';
        System.out.println(
                "输入 对应数字 进行相关操作"+EOF+
                "1. 读取文件"+EOF+
                "2. 保存当前数据"+EOF+
                "3. 增添课程"+EOF+
                "4. 增添学生课程信息"+EOF+
                "5. 显示课程最高分数学生信息"+EOF+
                "6. 显示课程平均分"+EOF+
                "7. 退出"+EOF
        );
    }
    public void showQuery(String item,QueryType qt){
        for(Stud s:StuList){
            if(Stud.getField(s,qt).equals(item)){
                System.out.println(s);
                for(Course c:table){
                    System.out.println(c.getCourseName()+"\tscore : "+c.queryScore(item));
                }
            }
        }
    }
    public void query(String item,QueryType qt){
        switch (qt) {
            case Subject -> table.forEach(e -> {
                if (e.getCourseName().equals(item))
                    System.out.println(e);
            });
            case ID, Name -> showQuery(item, qt);
        }
    }
    public boolean showTop(String courseName){
        for(Course c:table){
            if(c.getCourseName().equals(courseName)){
                Map.Entry<String,Double> id = c.getTopID();
                for (Stud e : StuList) {
                    if (e.getID().equals(id.getKey())) {
                        System.out.println("course : " + c.getCourseName() + '\n'
                                + e + '\n' + "score : " + id.getValue());
                        return true;
                    }
                }
            }
        }
        return false;
    }
    public boolean showNoPass(String courseName){//true if everyone pass
        for(Course c:table){
            if(c.getCourseName().equals(courseName)){
                TreeMap<String,Double> id = c.getNoPass();
                if(id.isEmpty())
                    return false;
                //a serial people who don't pass
                for (Stud e : StuList) {
                    if (id.containsKey(e.getID())) {
                        System.out.println("course : " + c.getCourseName() + '\n'
                                + e + '\n' + "score : " + id.get(e.getID()));
                    }
                }
            }
        }
        return true;
    }
    public void addCourse(String course){
        table.add(new Course(course));
    }
    public void addStudent(Stud s,String course,double score){
        IdtoName.put(s.getID(),s.getName());
        StuList.add(s);
        boolean get=false;
        for(Course c : table){
            if(c.getCourseName().equals(course)){
                get=true;
                c.add(s.getID(),score);
            }
        }if(!get){
            Course temp=new Course(course);
            temp.add(s.getID(),score);
            table.add(temp);
        }
    }
    public double courseAverage(String name){
        for (Course course : table) {
            if(course.getCourseName().equals(name))
                return course.Average();
        }return 0.0;
    }
    public void showCourseStage(String name){
        for (Course course : table) {
            if(course.getCourseName().equals(name)){
                int[] ints=course.range();
                System.out.println("score 0 : "+ints[0]+" people ");
                for (int i = 1; i < ints.length; i++) {
                    System.out.println("score "+(i*10-9)+" to "+(i*10)+" : "+ints[i]+" people ");
                }
            }
        }
    }
}
class Course{
    final private String courseName;
    private final TreeMap<String, Double> IDs;

    public String getCourseName() {
        return courseName;
    }
//    public static String getCourseName(){
//        return new Course().getCourseName();
//    }
    public Course(String name){
        this.courseName=name;
        IDs=new TreeMap<>();
    }
    public Course(){
        System.out.println("请输入课程名");
        this.courseName=new Scanner(System.in).next();
        IDs=new TreeMap<>();
    }
    public static double getValidScore(){
        Scanner scanner=new Scanner(System.in);
        double rel=-1;
        do{
            System.out.print("请输入合理成绩(0-100) : ");
            rel=scanner.nextDouble();
        }while(ValidScore(rel));
        return rel;
    }
    public static boolean ValidScore(double d){
        return d>=0&&d<=100;
    }
    public double queryScore(String field){
        return IDs.getOrDefault(field, Double.NaN);
    }
    public double Average(){
        double[] sum={0};
        IDs.forEach((k,v)->sum[0]+=v);
        return sum[0]/IDs.size();
    }
    public int[] range(){
        int[] r=new int[11];
        IDs.forEach((k,v)->{
            if(v==0)
                ++r[0];
            else if(v==100)
                ++r[10];
            else
                ++r[v.intValue()/10+1];
        });
        return r;
    }
    public Map.Entry<String,Double> getTopID(){
        Map.Entry<String,Double> ptr=IDs.firstEntry();
        for (Map.Entry<String, Double> entry : IDs.entrySet()) {
            if (ptr.getValue()<entry.getValue()){
                ptr=entry;
            }
        }
        return ptr;
    }
    public TreeMap<String,Double> getNoPass(){
        TreeMap<String,Double>noPass=new TreeMap<>();
        IDs.forEach((k,v)->{
            if(v<60){
                noPass.put(k,v);
        }});
        return noPass;
    }
    public void add(String id,double val){
        IDs.put(id,val);
    }
}
class Stud{
    private String ID;
    private String name;
    private void construct(String name,String id){
        this.name=name;
        this.ID=id;
    }
    public Stud(){
        Scanner scanner=new Scanner(System.in);
        System.out.print("please input your name: ");
        construct(scanner.next(),ValidID(scanner));
    }
    private String ValidID(Scanner scan){
        Pattern pattern=Pattern.compile("[1-9][0-9]\\{3}");
        Matcher matcher;
        String Valid;
        do{
            System.out.println("input valid student ID with the format : [1-9][0-9]{3}\n"+
                    "for example 2019 ; 1000");
            Valid = scan.next();
            matcher = pattern.matcher(Valid);
        }while(matcher.matches());
        return Valid;
    }
    public Stud(String name,String id){
        construct(name,id);
    }
    public String getName() {
        return name;
    }
    public String getID() {
        return ID;
    }
    public static String getField(Stud s,QueryType qt){
        return switch (qt) {
            case ID -> s.getID();
            case Name -> s.getName();
            default -> "";
        };
    }

    @Override
    public String toString() {
        return "{" +
                "ID : '" + ID + '\'' +
                ", name : '" + name + '\'' +
                '}';
    }
}